/** 
 * @Description: 圆柱光束材质
 * @Author: 程前
 * @Date: 2025-02-13 16:48:20
 * @LastEditors: 程前
 * @LastEditTime: 2025-02-20 14:53:51
 */

import { Color, Material, MaterialProperty, Event, defaultValue, defined } from "cesium";

interface MaterialCache {
    getMaterial: (type: string) => any;
    addMaterial: (type: string, material: any) => void;
}

interface CustomMaterial extends Material {
    CylinderBeamType?: string;
    _materialCache?: MaterialCache;
}

const materialType = 'CylinderBeam';
const material = Material as any as CustomMaterial;

export class CylinderBeamMaterialProperty implements MaterialProperty {
    private _baseColor: Color;
    private _lightColor: Color;
    private _speed: number;
    private _bandWidth: number;
    private _gradient: number;
    private _rotation: number;
    private _bands: number;
    private _definitionChanged: Event;

    constructor(options: {
        baseColor?: Color;
        lightColor?: Color;
        speed?: number;
        bandWidth?: number;
        gradient?: number;
        rotation?: number;
        bands?: number;
    }) {
        this._definitionChanged = new Event();
        this._baseColor = defaultValue(options.baseColor, new Color(1.0, 1.0, 0.0, 0.6));
        this._lightColor = defaultValue(options.lightColor, new Color(1.0, 1.0, 0.0, 1.0));
        this._speed = defaultValue(options.speed, 1.0);
        this._bandWidth = defaultValue(options.bandWidth, 0.1);
        this._gradient = defaultValue(options.gradient, 0.1);
        this._rotation = defaultValue(options.rotation, 0.0);
        this._bands = defaultValue(options.bands, 1.0);
    }

    getType(): string {
        return materialType;
    }

    getValue(time: any, result: any): any {
        if (!defined(result)) {
            result = {};
        }

        result.baseColor = this._baseColor;
        result.lightColor = this._lightColor;
        result.speed = this._speed;
        result.bandWidth = this._bandWidth;
        result.gradient = this._gradient;
        result.rotation = this._rotation;
        result.bands = this._bands;

        return result;
    }

    equals(other: any): boolean {
        return (
            this === other ||
            (other instanceof CylinderBeamMaterialProperty &&
                this._baseColor === other._baseColor &&
                this._lightColor === other._lightColor &&
                this._speed === other._speed &&
                this._bandWidth === other._bandWidth &&
                this._gradient === other._gradient &&
                this._rotation === other._rotation &&
                this._bands === other._bands)
        );
    }

    get isConstant(): boolean {
        return false;
    }

    get definitionChanged(): Event {
        return this._definitionChanged;
    }

    get baseColor(): Color {
        return this._baseColor;
    }

    set baseColor(value: Color) {
        if (Color.equals(this._baseColor, value)) {
            return;
        }
        this._baseColor = value;
        this._definitionChanged.raiseEvent(this);
    }

    get lightColor(): Color {
        return this._lightColor;
    }

    set lightColor(value: Color) {
        if (Color.equals(this._lightColor, value)) {
            return;
        }
        this._lightColor = value;
        this._definitionChanged.raiseEvent(this);
    }

    get speed(): number {
        return this._speed;
    }

    set speed(value: number) {
        if (this._speed === value) {
            return;
        }
        this._speed = value;
        this._definitionChanged.raiseEvent(this);
    }

    get bandWidth(): number {
        return this._bandWidth;
    }

    set bandWidth(value: number) {
        if (this._bandWidth === value) {
            return;
        }
        this._bandWidth = value;
        this._definitionChanged.raiseEvent(this);
    }

    get gradient(): number {
        return this._gradient;
    }

    set gradient(value: number) {
        if (this._gradient === value) {
            return;
        }
        this._gradient = value;
        this._definitionChanged.raiseEvent(this);
    }

    get rotation(): number {
        return this._rotation;
    }

    set rotation(value: number) {
        if (this._rotation === value) {
            return;
        }
        this._rotation = value;
        this._definitionChanged.raiseEvent(this);
    }

    get bands(): number {
        return this._bands;
    }

    set bands(value: number) {
        if (this._bands === value) {
            return;
        }
        this._bands = value;
        this._definitionChanged.raiseEvent(this);
    }
}

export const initCylinderBeamMaterial = () => {
    if (!material.CylinderBeamType) {
        material.CylinderBeamType = materialType;
    }

    if (!material._materialCache.getMaterial(materialType)) {
        material._materialCache.addMaterial(material.CylinderBeamType, {
            fabric: {
                type: material.CylinderBeamType,
                uniforms: {
                    baseColor: new Color(1.0, 1.0, 0.0, 0.6),
                    lightColor: new Color(1.0, 1.0, 0.0, 1.0),
                    speed: 1.0,
                    bandWidth: 0.1,
                    gradient: 0.1,
                    rotation: 0.0,
                    bands: 1.0
                },
                source: `czm_material czm_getMaterial(czm_materialInput materialInput) {
    czm_material material = czm_getDefaultMaterial(materialInput);
    vec2 st = materialInput.st;
    
    // 将坐标原点移到中心
    vec2 uv = st - 0.5;
       // 获取法线（模型坐标系下）
        vec3 normal = materialInput.normalEC; // 或 normalMC
        // 判断是否为底面 假设底面法线朝下 接近负Z轴
        if (dot(normal, vec3(0, 0, -1)) > 0.99) {
          discard;
        }
    // 应用旋转 rotatedUV旋转过后的uv坐标系
    float cosA = cos(rotation);
    float sinA = sin(rotation);
    vec2 rotatedUV = vec2(
        uv.x * cosA - uv.y * sinA,
        uv.x * sinA + uv.y * cosA
    ); 
    
    // 计算到中心的距离 使用max范数得到正方形
    float dist = max(abs(rotatedUV.x), abs(rotatedUV.y));
    
    // 创建扩散动画
    float time = czm_frameNumber * speed * 0.01;
    float wave = fract(dist * 2.0 - time);
    
    // 创建多个光带
    wave = fract(wave * bands);
    
    // 创建光带
    float band = smoothstep(0.0, gradient, wave) * smoothstep(bandWidth, 0.0, wave);
    
    // 混合基础颜色和光带颜色
    vec3 color = mix(baseColor.rgb, lightColor.rgb, band);
    float alpha = mix(baseColor.a, lightColor.a, band);
    
    // 设置最终颜色
    material.diffuse = color;
    material.alpha = alpha;
    
    return material;
}`
            },
            translucent: function () {
                return true;
            }
        });
    }
}; 